package pipeline

import (
	"alarm-flow/models"
	"alarm-flow/pkg/influxdbtool"
	"fmt"
	"log/slog"
	"reflect"
	"strconv"
)

type AlarmEntry struct {
	Reason      models.AlarmReason
	Measurement string
	Tags        []influxdbtool.TagKeyValue
	FieldKey    string
	FieldValue  interface{}
}

func DealEntry(entryChan <-chan influxdbtool.LineEntry, alarmChan chan<- AlarmEntry) {

	var err error
	var ok bool
	var alarmConfig *RedisAlarmConfig
	var cmdbHostId int64
	var measurementFields []string

	for entry := range entryChan {
		if entry.Measurement == "host_cpu_usage" {
			slog.Debug("receive entry", "entry", entry)
		}

		v := entry.Tag("cmdb_inst_id")
		cmdbHostId, _ = strconv.ParseInt(v, 10, 64)
		if cmdbHostId == 0 {
			slog.Error("parse cmdb_inst_id error", "cmdbHostId", v)
			continue
		}
		// todo：应当考虑将配置加载到本地内存里，避免频繁地从redis取值反序列化

		measurementFields, err = getMeasurementFields(entry.Measurement)
		if err != nil {
			slog.Error("get measurement fields error", "measurement", entry.Measurement, "error", err)
			continue
		}

		for _, field := range measurementFields {
			alarmConfig, err = getAlarmConfig(entry.Measurement, field)
			if err != nil {
				continue
			}

			collectFieldValue := entry.Field(field)
			if collectFieldValue == nil {
				slog.Error("can not find measurement", "measurement", entry.Measurement, "field", field)
				continue
			}

			// todo: 多个条件下，同一指标的优先级问题
			for _, condition := range alarmConfig.Conditions {
				ok, err = TriggerAlarm(collectFieldValue, &condition)
				if err != nil {
					slog.Error("can not compare condition", "measurement", entry.Measurement, "field", field, "error", err)
					continue
				}
				if ok {
					alarmChan <- AlarmEntry{
						Reason:      models.Alarm,
						Measurement: entry.Measurement,
						Tags:        entry.Tags,
						FieldKey:    field,
						FieldValue:  collectFieldValue,
					}
					err = setInstState(cmdbHostId, entry.Measurement, field, "alarming")
					if err != nil {
						slog.Error("can not set inst state", "measurement", entry.Measurement, "field", field)
					}
				} else {
					// 从告警中恢复
					state, err2 := getInstState(cmdbHostId, entry.Measurement, field)
					if err2 != nil {
						slog.Error("can not get state", "measurement", entry.Measurement, "field", field)
					}

					err = setInstState(cmdbHostId, entry.Measurement, field, "normal")
					if err != nil {
						slog.Error("can not set inst state", "measurement", entry.Measurement, "field", field)
					}

					if state != nil && state.State == "alarming" {
						alarmChan <- AlarmEntry{
							Reason:      models.Recovery,
							Measurement: entry.Measurement,
							Tags:        entry.Tags,
							FieldKey:    field,
							FieldValue:  collectFieldValue,
						}
					}
				}
			}
		}
	}
}

func TriggerAlarm(value interface{}, condition *models.AlarmCondition) (ok bool, err error) {
	var collectValueFloat, thresholdFloat float64
	var collectValueString, thresholdString string

	switch value.(type) {
	case string:
		collectValueString = value.(string)
		thresholdString = condition.Value
	case uint64:
		collectValueFloat = float64(value.(uint64))
	case int64:
		collectValueFloat = float64(value.(int64))
	case float64:
		collectValueFloat = value.(float64)
	default:
		return false, fmt.Errorf("value type not supported: %v", reflect.TypeOf(value))
	}

	thresholdFloat, err = strconv.ParseFloat(condition.Value, 64)

	switch condition.Operator {
	case ">":
		ok = collectValueFloat > thresholdFloat
		return
	case ">=":
		ok = collectValueFloat >= thresholdFloat
		return
	case "=":
		ok = collectValueFloat == thresholdFloat
		return
	case "<":
		ok = collectValueFloat < thresholdFloat
		return
	case "<=":
		ok = collectValueFloat <= thresholdFloat
		return
	case "!=":
		if thresholdString != "" {
			ok = collectValueString != thresholdString
			return
		}

		ok = collectValueFloat != thresholdFloat
		return
	default:
		slog.Error("unknown condition operator", "operator", condition.Operator)
		err = fmt.Errorf("unknown condition operator: %s", condition.Operator)
		return
	}
}

func DealAlarm(alarmChan <-chan AlarmEntry) {
	for alarm := range alarmChan {
		fmt.Println("alarm >>> ", alarm)
	}
}
